package com.sicent.operate.common.util;

import java.io.IOException;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.SocketTimeoutException;
import java.net.URLEncoder;
import java.nio.charset.CodingErrorAction;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.MessageConstraints;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

public class HttpClientUtil {
     
    private final static Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);
     
    private static PoolingHttpClientConnectionManager connManager = null;
    
    private static CloseableHttpClient httpclient = null;
    
    public final static int connectTimeout = 300000;
     
    static {
        try {
            SSLContext sslContext = SSLContexts.custom().useTLS().build();
            sslContext.init(null,
                    new TrustManager[] { new X509TrustManager() {
                         
                        public X509Certificate[] getAcceptedIssuers() {
                            return null;
                        }
 
                        public void checkClientTrusted(
                                X509Certificate[] certs, String authType) {
                        }
 
                        public void checkServerTrusted(
                                X509Certificate[] certs, String authType) {
                        }
                    }}, null);
            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.INSTANCE)
                    .register("https", new SSLConnectionSocketFactory(sslContext))
                    .build();
             
            connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
            SocketConfig socketConfig = SocketConfig.custom().setTcpNoDelay(true).build();
            connManager.setDefaultSocketConfig(socketConfig);
            MessageConstraints messageConstraints = MessageConstraints.custom()
                .setMaxHeaderCount(200)
                .setMaxLineLength(2000)
                .build();
            ConnectionConfig connectionConfig = ConnectionConfig.custom()
                .setMalformedInputAction(CodingErrorAction.IGNORE)
                .setUnmappableInputAction(CodingErrorAction.IGNORE)
                .setCharset(Consts.UTF_8)
                .setMessageConstraints(messageConstraints)
                .build();
            connManager.setDefaultConnectionConfig(connectionConfig);
            connManager.setMaxTotal(200);
            connManager.setDefaultMaxPerRoute(20);
            httpclient = HttpClients.custom().setConnectionManager(connManager).build();
        } catch (KeyManagementException e) {
            logger.error("KeyManagementException", e);
        } catch (NoSuchAlgorithmException e) {
            logger.error("NoSuchAlgorithmException", e);
        }
    }
     
    public static Serializable executeHttpGet(String url, Map<String, String> paramMap, Class<? extends Serializable> clazz) {
        Serializable response = null;
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(connectTimeout)
                .setConnectTimeout(connectTimeout)
                .setConnectionRequestTimeout(connectTimeout).build();
        StringBuilder sb = new StringBuilder();
        sb.append(url);
        int i = 0;
        for (String key : paramMap.keySet()) {
            if (i == 0 && !url.contains("?")) {
                sb.append("?");
            } else {
                sb.append("&");
            }
            sb.append(key);
            sb.append("=");
            String value = paramMap.get(key);
            try {
                sb.append(URLEncoder.encode(value, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                logger.warn("encode http get params error, value is "+value, e);
            }
            i++;
        }
        HttpGet get = new HttpGet(sb.toString());
        get.setConfig(requestConfig);
        try {
            CloseableHttpResponse httpResponse = httpclient.execute(get);
            try {
                HttpEntity entity = (HttpEntity) httpResponse.getEntity();
                try {
                    if (entity != null) {
                    	//System.out.println(EntityUtils.toString(httpResponse.getEntity()));
                        response = JSON.parseObject(EntityUtils.toString(entity, Consts.UTF_8), clazz);
                    }
                } finally {
                    if (entity != null) {
                        entity.getContent().close();
                    }
                }
            } catch (Exception e) {
                logger.error(String.format("[HttpUtils Get]get response error, url:%s", sb.toString()), e);
                return response;
            } finally {
                if (httpResponse != null) {
                    httpResponse.close();
                }
            }
        } catch (SocketTimeoutException e) {
            logger.error(String.format("[HttpUtils Get]invoke get timout error, url:%s", sb.toString()), e);
            return response;
        } catch (Exception e) {
            logger.error(String.format("[HttpUtils Get]invoke get error, url:%s", sb.toString()), e);
        } finally {
            get.releaseConnection();
        }
        return response;
    }
    
    public static Serializable executeHttpPostWithParamBody(String url, Map<String, String> paramMap, Class<? extends Serializable> clazz){
        Serializable response = null;
        HttpPost post = new HttpPost(url);
        try {
            RequestConfig requestConfig = RequestConfig.custom()
                    .setSocketTimeout(connectTimeout)
                    .setConnectTimeout(connectTimeout)
                    .setConnectionRequestTimeout(connectTimeout)
                    .setExpectContinueEnabled(false).build();
            post.setConfig(requestConfig);
            StringBuilder sb = new StringBuilder("{");
    		for (String key : paramMap.keySet()) {
    			sb.append("\"").append(key).append("\"").append(":").append("\"").append(paramMap.get(key)).append("\"").append(",");
    		}
    		sb.deleteCharAt(sb.lastIndexOf(","));
    		sb.append("}");
    		StringEntity reqEntity = new StringEntity(sb.toString(), Consts.UTF_8); 
			reqEntity.setContentType("text/plain");
            post.setEntity(reqEntity);
            CloseableHttpResponse httpResponse = httpclient.execute(post);
            try {
                HttpEntity entity = httpResponse.getEntity();
                try {
                    if (entity != null) {
                        response = (Serializable) JSON.parseObject(EntityUtils.toString(httpResponse.getEntity(), Consts.UTF_8), clazz);
                    }
                } finally {
                    if (entity != null) {
                        entity.getContent().close();
                    }
                }
            } finally {
                if (httpResponse != null) {
                	httpResponse.close();
                }
            }
        } catch (UnsupportedEncodingException e) {
            logger.error("UnsupportedEncodingException", e);
        } catch (Exception e) {
            logger.error("Exception", e);
        } finally {
            post.releaseConnection();
        }
        return response;
    }
     
    public static Serializable executeHttpPostWithNameValuePair(String urlPath, Map<String, String> paramMap, Class<? extends Serializable> clazz) {
        Serializable response = null;
        HttpPost httpPost = new HttpPost(urlPath); 
        try {
            RequestConfig requestConfig = RequestConfig.custom()
                    .setSocketTimeout(connectTimeout)
                    .setConnectTimeout(connectTimeout)
                    .setConnectionRequestTimeout(connectTimeout).build();
            List<NameValuePair> postParams = new ArrayList<NameValuePair>();
    		for (String key : paramMap.keySet()) {
    			postParams.add(new BasicNameValuePair(key, paramMap.get(key)));
    		} 
            httpPost.setEntity(new UrlEncodedFormEntity(postParams, Consts.UTF_8));
            httpPost.setConfig(requestConfig);
            CloseableHttpResponse httpResponse = httpclient.execute(httpPost);
             try {
                HttpEntity entity = httpResponse.getEntity();
                try {
                    if (null != entity) {
//                    	System.out.println(EntityUtils.toString(httpResponse.getEntity()));
                    	response = (Serializable) JSON.parseObject(EntityUtils.toString(httpResponse.getEntity(), Consts.UTF_8), clazz);
                    }
                } finally {
                    if (entity != null) {
                        entity.getContent().close();
                    }
                }
            } finally {
                if (httpResponse != null) {
                	httpResponse.close();
                }
            }
        } catch (ClientProtocolException e) {
            logger.error("ClientProtocolException", e);
        } catch (IOException e) {
            logger.error("IOException", e);
        } finally {
            httpPost.releaseConnection();
        }
        return response;
    }
    
    public static String executeHttpPostWithNameValuePair(String urlPath, Map<String, String> paramMap) {
        String response = null;
        HttpPost httpPost = new HttpPost(urlPath); 
        try {
            RequestConfig requestConfig = RequestConfig.custom()
                    .setSocketTimeout(connectTimeout)
                    .setConnectTimeout(connectTimeout)
                    .setConnectionRequestTimeout(connectTimeout).build();
            List<NameValuePair> postParams = new ArrayList<NameValuePair>();
    		for (String key : paramMap.keySet()) {
    			postParams.add(new BasicNameValuePair(key, paramMap.get(key)));
    		} 
            httpPost.setEntity(new UrlEncodedFormEntity(postParams, Consts.UTF_8));
            httpPost.setConfig(requestConfig);
            CloseableHttpResponse httpResponse = httpclient.execute(httpPost);
             try {
                HttpEntity entity = httpResponse.getEntity();
                try {
                    if (null != entity) {
                    	response = EntityUtils.toString(httpResponse.getEntity(), Consts.UTF_8);
                    }
                } finally {
                    if (entity != null) {
                        entity.getContent().close();
                    }
                }
            } finally {
                if (httpResponse != null) {
                	httpResponse.close();
                }
            }
        } catch (ClientProtocolException e) {
            logger.error("ClientProtocolException", e);
        } catch (IOException e) {
            logger.error("IOException", e);
        } finally {
            httpPost.releaseConnection();
        }
        return response;
    }
    
    public static Serializable executeHttpPostWithStream(String requestUrl, String stream, Class<? extends Serializable> clazz) {
    	Serializable response = null;
        HttpPost post = new HttpPost(requestUrl);
        try {
            RequestConfig requestConfig = RequestConfig.custom()
                    .setSocketTimeout(connectTimeout)
                    .setConnectTimeout(connectTimeout)
                    .setConnectionRequestTimeout(connectTimeout)
                    .setExpectContinueEnabled(false).build();
            post.setConfig(requestConfig);
    		StringEntity reqEntity = new StringEntity(stream, Consts.UTF_8); 
			reqEntity.setContentType("application/x-www-form-urlencoded");
            post.setEntity(reqEntity);
            CloseableHttpResponse httpResponse = httpclient.execute(post);
            try {
                HttpEntity entity = httpResponse.getEntity();
                try {
                    if (entity != null) {
                        response = (Serializable) JSON.parseObject(EntityUtils.toString(httpResponse.getEntity(), Consts.UTF_8), clazz);
                    }
                } finally {
                    if (entity != null) {
                        entity.getContent().close();
                    }
                }
            } finally {
                if (httpResponse != null) {
                	httpResponse.close();
                }
            }
        } catch (UnsupportedEncodingException e) {
            logger.error("UnsupportedEncodingException", e);
        } catch (Exception e) {
            logger.error("Exception", e);
        } finally {
            post.releaseConnection();
        }
        return response;
    }
 
    public static Serializable executeHttpPostWithJSON(String requestUrl, JSONObject json) {
    	Serializable response = null;
        HttpPost post = new HttpPost(requestUrl);
        try {
            RequestConfig requestConfig = RequestConfig.custom()
                    .setSocketTimeout(connectTimeout)
                    .setConnectTimeout(connectTimeout)
                    .setConnectionRequestTimeout(connectTimeout)
                    .setExpectContinueEnabled(false).build();
            post.setConfig(requestConfig);
    		StringEntity reqEntity = new StringEntity(json.toString(), Consts.UTF_8); 
			reqEntity.setContentType("application/json");
            post.setEntity(reqEntity);
            CloseableHttpResponse httpResponse = httpclient.execute(post);
            try {
                HttpEntity entity = httpResponse.getEntity();
                try {
                    if (entity != null) {
                        response = (Serializable) JSON.parseObject(EntityUtils.toString(httpResponse.getEntity(), Consts.UTF_8), JSONObject.class);
                    }
                } finally {
                    if (entity != null) {
                        entity.getContent().close();
                    }
                }
            } finally {
                if (httpResponse != null) {
                	httpResponse.close();
                }
            }
        } catch (UnsupportedEncodingException e) {
            logger.error("UnsupportedEncodingException", e);
        } catch (Exception e) {
            logger.error("Exception", e);
        } finally {
            post.releaseConnection();
        }
        return response;
    }
    
    public static void main(String[] args) {
		JSONObject json = new JSONObject();
		json.put("snbid", "jswx-196");
		JSONObject result = (JSONObject) executeHttpPostWithJSON("http://localhost:8080/008-web/l2/internetbar!queryInternetBarAccount.do?userName=admin&password=123456", json);
		System.out.println(result);
	}
}